package com.dycong.common.Lamda;

import org.junit.Test;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.*;
import java.util.stream.*;

import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;

/**
 * Created by dycong on 2016/12/7.
 */
public class MyStream {

    List<Integer> list = Stream.generate(() -> {
        return new Random().nextInt();
    }).limit(350).collect(toList());
    Integer[] array = Stream.generate(() -> new Random().nextInt()).limit(350).toArray(Integer[]::new);
    Integer[] temp = new Random().ints(0, 9999).limit(350).boxed().toArray(Integer[]::new);


    public void test() {
        System.out.println(list.stream().count());
        System.out.println(list.stream().mapToInt(o -> o.intValue()).sum());
        System.out.println(list.stream().mapToInt(o -> o.intValue()).max());
        System.out.println(list.stream().mapToInt(o -> o.intValue()).average());
        System.out.println(list.stream().mapToInt(o -> o.intValue()).max().getAsInt());
        System.out.println(list.stream().mapToInt(o -> o.intValue()).sorted().boxed().collect(Collectors.toSet()));
        System.out.println(list.stream().mapToInt(o -> o.intValue()).average().getAsDouble());
    }

    public void test0() {
        Stream.of("a", "b", "c", "d", "e", "f");
        Arrays.stream(array);
        Arrays.asList(array).stream();
        List<Double> list = new Random().doubles(10000).boxed().collect(toList());
        List integerList = Stream.generate(() -> {
            return new Random().nextInt();
        }).limit(10).collect(toList());
        Stream stream = Arrays.stream(array);
        stream.forEach(o -> System.out.println(o));
        /*todo 此处会报错，一个 Stream 只可以使用一次，一个流只能有一个 terminal 操作，当这个操作执行后，流就被使用“光”了，无法再被操作。所以这必定是流的最后一个操作。*/
        stream.forEach(o -> System.out.println(o));
        /**
         * 从 BufferedReader
         java.com.dycong.common.IO.socket.BufferedReader.lines()
         静态工厂
         java.util.stream.IntStream.range()
         java.nio.file.Files.walk()
         自己构建
         java.util.Spliterator
         其它
         Random.ints()
         BitSet.stream()
         Pattern.splitAsStream(java.lang.CharSequence)
         JarFile.stream()
         */
    }


    public void test1() {
        System.out.println(IntStream.of(new int[]{1, 2, 3, 4, 5, 6}));
        List list1 = IntStream.range(1, 9999).limit(350).boxed().collect(toCollection(ArrayList::new));
        List list2 = LongStream.range(1, 9999).limit(350).boxed().collect(toCollection(ArrayList::new));
        System.out.println(DoubleStream.of(new double[]{1.0d, 2.0d}));
        System.out.println(DoubleStream.generate(() -> new Random().nextDouble()).limit(350).boxed().collect(Collectors.toSet()));
    }


    @Test
    public void test2() {
        String string = list.stream().map(o -> o + "").collect(Collectors.joining(",", "start: ", "!!"));

        Stream<List<Integer>> inputStream = Stream.of(
                Arrays.asList(1),
                Arrays.asList(2, 3),
                Arrays.asList(4, 5, 6)
        );
        Stream<List<Integer>> inputStream1 = Stream.of(
                Arrays.asList(1),
                Arrays.asList(2, 3),
                Arrays.asList(4, 5, 6)
        );

        /*todo 得到的是List<List>结构数据*/
//        List list = inputStream.collect(Collectors.toList());
//        list.stream().forEach(o -> System.out.println(o));


        /*todo flatMap 把 input Stream 中的层级结构扁平化，就是将最底层元素抽出来放到一起，最终 output 的新 Stream 里面已经没有 List 了，都是直接的数字。*/
        /*flatMap()可以生成新的Stream*/
        Stream<Integer> outputStream = inputStream.
                flatMap((childList) -> childList.stream());

        List<Integer> list1 = outputStream.collect(toList());
        System.out.println(list);

        /*给定两个数字列表，如何返回所有的数对呢？例如，给定列表[1, 2, 3]和列表[3, 4]，应该返回[(1, 3), (1, 4), (2, 3), (2, 4), (3, 3), (3, 4)]。为简单起见，你可以用有两个元素的数组来代表数对*/
        List<Integer> numbers1 = Arrays.asList(1, 2, 3);
        List<Integer> numbers2 = Arrays.asList(3, 4);
        /*TODO Stream接口也是class自然可以作为返回值，数组是一种特殊的类型，基本类型数组可以作为泛型里的参数*/
        List<Stream<int[]>> list2 = numbers1.stream().map(integer -> numbers2.stream().map(j -> new int[]{integer, j})).collect(toList());
        List<List<int[]>> list3 = numbers1.stream().map(integer -> numbers2.stream().map(j -> new int[]{integer, j}).collect(toList())).collect(toList());
        /*todo flatMap生成的流扁平化，以得到一个Stream<Integer[]>*/
        List<int[]> list4 = numbers1.stream().flatMap(integer -> numbers2.stream().map(j -> new int[]{integer, j})).collect(toList());
        System.out.println();
    }


    public void test3() {
        Stream stream = Arrays.stream(array);
        stream.forEach(element -> System.out.println(element));
        /*todo forEach 是 terminal 操作，因此它执行后，Stream 的元素就被“消费”掉了，你无法对一个 Stream 进行两次 terminal 运算。*/
//        stream.forEach(element -> System.out.println(element));


        /*todo 具有相似功能的 intermediate 操作 peek 可以达到上述目的,peek为intermediate操作，不会“消耗掉”stream流！！
         * peek 对每个元素执行操作并返回一个新的 Stream*/
        Stream.of("1", "2", "3", "4")
                .filter(e -> e.length() > 0)
                .peek(e -> System.out.println("Filtered value: " + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("Mapped value: " + e))
                .collect(toList());
    }

    public void test4() {
        list = list.stream().sorted().collect(toList());

        /*当需要为多核系统优化时，可以 parallelStream().forEach()，只是此时原有元素的次序没法保证，并行的情况下将改变串行时操作的行为，此时 forEach 本身的实现不需要调整*/
        list.parallelStream().peek(o -> System.out.println(Thread.currentThread())).forEach(o -> System.out.println(o));

        /*stream(array)并不使用并发,只是此时原有元素的次序有保证*/
        Arrays.stream(array).sorted().peek(o -> System.out.println(o)).forEach(o -> System.out.println(Thread.currentThread()));
    }

    public void test5() {
        Integer integer = list.stream().findFirst().orElse(null);

        /*找到一个就返回*/
        Integer bool = list.stream().filter(o -> o > 3500).findAny().orElse(null);
        System.out.print(integer + "---" + bool);
    }

    public void test6() {
        /*字符串连接，concat = "ABCD"*/
        String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat);
        /*todo 求最小值，minValue = -3.0;起始值为Double.MAX_VALUE*/
        double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min);
        double min = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(-9D, Double::min);
        /*求和，sumValue = 10, 有起始值*/
        int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum);
        /*求和，sumValue = 10, 无起始值:todo 第一个示例的 reduce()，第一个参数（空白字符）即为起始值，第二个参数（String::concat）为 BinaryOperator。
        todo 这类有起始值的 reduce() 都返回具体的对象。而对于第四个示例没有起始值的 reduce()，由于可能没有足够的元素，返回的是 Optional，请留意这个区别。*/
        sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get();
        /*过滤，字符串连接，concat = "ace"*/
        concat = Stream.of("a", "B", "c", "D", "e", "F").
                filter(x -> x.compareTo("Z") > 0).
                reduce("", String::concat);
    }

    /**
     * 对 Stream 的排序通过 sorted 进行，它比数组的排序更强之处在于你可以首先对
     * todo Stream 进行各类 map、filter、limit、skip 甚至 distinct 来减少元素数量后，再排序，这能帮助程序明显缩短执行时间。
     */

    public void test7() {

        /**todo 第一个peek()输出无序的350个数，代表sort()进行了350次，第二个peek()输出排序完成以后的前十个（最小十个）
         *即虽然最后的返回元素数量是 10，但整个管道中的 sorted 表达式执行次数没有相应减少。最后有一点需要注意的是*/
        /*todo 对一个 parallel 的 Steam 管道来说，如果其元素是有序的，那么 limit 操作的成本会比较大，因为它的返回对象必须是前 n 个也有一样次序的元素。取而代之的策略是取消元素间的次序，或者不要用 parallel Stream。*/
        List tmp = list.stream().peek(o -> System.out.println(o)).sorted((o1, o2) -> o1.compareTo(o2)).limit(10).peek(o -> System.out.println(o)).collect(toList());
        /*输出为未排序的前十个，第二个peek()输出这十个排好序的状态*/
        /*todo 当limit，skip()在sort()之前时会影响到sort的执行次数：先拿未排序时的前十个，排序*/
        List tmp1 = list.stream().limit(10).peek(o -> System.out.println(o)).sorted((o1, o2) -> o1.compareTo(o2)).peek(o -> System.out.println(o)).collect(toList());

        System.out.println(tmp1);
    }


    public void test8() {
        /**todo IO流在被读取之后也会“被消耗”，也是只能用一次*/
        try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Duke\\Desktop\\2016.09-\\Test-Java\\stream.java"))) {

            /*找出最长一行的长度*/
            int longest = br.lines().
                    mapToInt(String::length).
                    max().
                    getAsInt();
            System.out.println(longest);

        } catch (IOException ex) {
            System.out.println(ex);
        }


        try (BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\Duke\\Desktop\\2016.09-\\Test-Java\\stream.java"))) {

            /*找出全文的单词，转小写，并排序*/
            List<String> words = br.lines().
                    flatMap(line -> Stream.of(line.split(" "))).
                    filter(word -> word.length() > 0).
                    map(String::toLowerCase).
                    distinct().
                    sorted().
                    collect(toList());
            System.out.println(words);

        } catch (IOException ex) {
            System.out.println(ex);
        }
    }


    public void test9() {
        /*todo 它们都不是要遍历全部元素才能返回结果。例如 allMatch 只要一个元素不满足条件，就 skip 剩下的所有元素，返回 false。*/

        System.out.println(list.stream().limit(100).allMatch(temp -> temp > 524));
        System.out.println(list.stream().limit(100).allMatch(temp -> temp > Integer.MIN_VALUE));

        System.out.println(list.stream().limit(100).anyMatch(temp -> temp > 524));

        System.out.println(list.stream().limit(100).noneMatch(temp -> temp > 524));
        System.out.println(list.stream().limit(100).noneMatch(temp -> temp > Integer.MAX_VALUE));
    }


    public void test10() {
        /*iterate 跟 reduce 操作很像，接受一个种子值，和一个 UnaryOperator（例如 f）。然后种子值成为 Stream 的第一个元素，f(seed) 为第二个，f(f(seed)) 第三个，以此类推。*/
        /*todo 不同之处reduce是Terminal操作，而iterate是Intermediate操作*/
        Stream.iterate(0, n -> n + 3).limit(10).forEach(x -> System.out.print(x + " "));

        /*生成一个等差数列*/
        List list = Stream.iterate(0, n -> n + 3).limit(10).collect(toList());
        System.out.println(list);
    }

    @Test
    public void test11() {
        //todo 获取数字的个数、最小值、最大值、总和以及平均值
        List<Integer> primes = Arrays.asList(2, 3, 5, 7, 11, 13, 17, 19, 23, 29);
        IntSummaryStatistics stats = primes.stream().mapToInt((x) -> x).summaryStatistics();
        System.out.println("Highest prime number in List : " + stats.getMax());
        System.out.println("Lowest prime number in List : " + stats.getMin());
        System.out.println("Sum of all prime numbers : " + stats.getSum());
        System.out.println("Average of all prime numbers : " + stats.getAverage());
    }

    @Test
    public void test12() {
        List<Integer> primes = new ArrayList<>(100);
        for (int i = 0; i < 1000; i++) {
            primes.add(i);
        }
        primes.parallelStream().forEach(System.out::println);
    }

    @Test
    public void test13() {
        List<Integer> list = new Random().ints(10000).boxed().collect(toList());

        list.forEach(o -> {
            if (o % 2 == 0) {
                return;
            }
            System.out.println(o);
        });

    }
    // TODO: 2019/4/10 StreamSupport 待研究 
/*
    @Test
    public void test13() {
        List<Integer> primes = new ArrayList<>(100);
        for (int i = 0; i < 1000; i++) {
            primes.add(i);
        }


        Collectors.collectingAndThen(
                toList(),
                list -> StreamSupport.stream(
                        new ShuffledSpliterator<>(list), false));
    }


    public static <T> Collector<T, ?, Stream<T>> toEagerShuffledStream() {
        return Collectors.collectingAndThen(
                toList(),
                list -> {
                    Collections.shuffle(list);
                    return list.stream();
                });
    }

    public static <T> Collector<T, ?, Stream<T>> toLazyShuffledStream() {
        return Collectors.collectingAndThen(
                toCollection(ArrayList::new),
                list -> !list.isEmpty()
                        ? StreamSupport.stream(new RandomSpliterator<>(list, Random::new), false)
                        : Stream.empty());
    }
}

public class RandomSpliterator<T> implements Spliterator<T> {
    // ...
    public static <T> Collector<T, ?, Stream<T>> toLazyShuffledStream() {
        return Collectors.collectingAndThen(
                toList(),
                list -> StreamSupport.stream(
                        new ShuffledSpliterator<>(list), false));
    }

    @Override
    public boolean tryAdvance(Consumer<? super T> action) {
        return false;
    }

    @Override
    public Spliterator<T> trySplit() {
        return null;
    }

    @Override
    public long estimateSize() {
        return 0;
    }

    @Override
    public int characteristics() {
        return 0;
    }
*/
}
